import dxPwm from '../dxmodules/dxPwm.js'
import std from '../dxmodules/dxStd.js'
import dxNet from '../dxmodules/dxNet.js'
import dxGpio from '../dxmodules/dxGpio.js'
import dxCode from '../dxmodules/dxCode.js'
import dxNfc from '../dxmodules/dxNfc.js'
import dxAlsaplay from '../dxmodules/dxAlsaplay.js'
import dxGpioKey from '../dxmodules/dxGpioKey.js'
import dxNtp from '../dxmodules/dxNtp.js'
import dxMap from '../dxmodules/dxMap.js'
import config from '../dxmodules/dxConfig.js'
import common from '../dxmodules/dxCommon.js'
import dxUart from '../dxmodules/dxUart.js'
import watchdog from '../dxmodules/dxWatchdog.js'
import utils from './common/utils/utils.js'
import uartBleService from './service/uartBleService.js'
import dxTcp from '../dxmodules/dxTcp.js'
import log from '../dxmodules/dxLogger.js'
import bus from '../dxmodules/dxEventBus.js'
import http from '../dxmodules/dxHttp.js'
import netProService from './service/netProService.js'
import safeService from './service/safeService.js'
import * as os from "os"
const driver = {}

driver.pwm = {
    init: function () {
        // Initialize buzzer
        dxPwm.request(4);
        dxPwm.setPeriodByChannel(4, 366166)
        dxPwm.enable(4, true)
        common.systemBrief("echo " + (config.get("sysInfo.backlight") <= 0 ? 1 : Math.floor(config.get("sysInfo.backlight") * 15 / 100)) + " > /sys/class/backlight/backlight/brightness")
    },
    // Keypad Tones
    press: function () {
        dxPwm.beep({ channel: 4, time: 30, volume: config.get("sysInfo.volume1"), interval: 0 })
    },
    // failure sound
    fail: function () {
        dxPwm.beep({ channel: 4, time: 500, volume: utils.getVolume1(config.get("sysInfo.volume1")), interval: 0 })
    },
    // Success sound
    success: function () {
        dxPwm.beep({ channel: 4, time: 30, count: 2, volume: utils.getVolume1(config.get("sysInfo.volume1")) })
    },
    // Warning tone
    warning: function () {
        dxPwm.beep({ channel: 4, volume: utils.getVolume1(config.get("sysInfo.volume1")), interval: 0 })
    },
    // Custom buzzer
    beep: function (time, interval, count) {
        let volume = utils.getVolume1(config.get("sysInfo.volume1"))
        time = time ? time : config.get('sysInfo.beepd')
        std.setTimeout(() => {
            for (let i = 0; i < count; i++) {
                dxPwm.setDutyByChannel(4, 366166 * volume / 255)
                os.sleep(time)
                dxPwm.setDutyByChannel(4, 0)
                if (i < (count - 1)) {
                    // The last beep was without interruption
                    os.sleep(interval)
                }
            }
        }, 100)
    },
}
driver.net = {
    init: function () {
        if (!config.get('sysInfo.net_type')) {
            log.debug("The network is closed.")
            return
        }
        std.setTimeout(() => {
            dxNet.worker.beforeLoop(getNetOptions())
            if (config.get("sysInfo.net_type") == 2) {
                dxNet.netConnectWifiSsid(config.get("sysInfo.ssid"), config.get("sysInfo.psk"), "")
            }
        }, 3000)
    },
    loop: function () {
        if (!config.get('sysInfo.net_type')) {
            log.debug("The network is closed.")
            this.loop = () => { }
        } else {
            this.loop = () => dxNet.worker.loop()
        }
    },
    getStatus: function () {
        let status = dxNet.getStatus()
        if (status.connected == true && status.status == 4) {
            return true
        } else {
            return false
        }

    },
}

// Get net connection configuration
function getNetOptions () {
    let dhcp = config.get("sysInfo.ip_mode")
    dhcp = utils.isEmpty(dhcp) ? dxNet.DHCP.DYNAMIC : (dhcp == 0 ? 2 : 1)
    let dns = config.get("sysInfo.dns")
    dns = utils.isEmpty(dns) ? [null, null] : dns.split(",")
    let ip = config.get("sysInfo.ip")
    let type = config.get("sysInfo.net_type")
    let fixed_macaddr_enable = config.get('sysInfo.fixed_macaddr_enable') || 0
    let macaddr = config.get('sysInfo.macaddr') || common.getUuid2mac()

    if (utils.isEmpty(ip)) {
        // If no IP address is set, a dynamic IP address will be used.
        dhcp = dxNet.DHCP.DYNAMIC
    }
    let options = {
        type: type,
        dhcp: dhcp,
        ip: ip,
        gateway: config.get("sysInfo.gateway"),
        netmask: config.get("sysInfo.mask"),
        dns0: dns[0],
        dns1: dns[1],
        macAddr: fixed_macaddr_enable == 2 ? macaddr : common.getUuid2mac(),
    }
    return options
}

driver.gpio = {
    init: function () {
        dxGpio.init()
        dxGpio.request(35)
    },
    relay: function (status, time) {
        //Determine whether to enable the security module
        if (config.get('sysInfo.safe_open') === 1) {
            safeService.open();
        } else {
            dxGpio.setValue(35, status)
            if (status) {
                // Timer-off relay
                std.setTimeout(() => dxGpio.setValue(35, 0), !utils.isEmpty(time) ? time : config.get('sysInfo.relayd'))
            }
        }
    },
    always: function () {
        if (config.get('sysInfo.safe_open') === 1) {
            safeService.open();
        } else {
            dxGpio.setValue(35, 1)
        }

    }
}

driver.code = {
    options1: { id: 'capturer1', path: '/dev/video11', capturerDogId: "watchdog"  },
    options2: { id: 'decoder1', name: "decoder v4", width: 800, height: 600 },
    init: function () {
        if (!config.get('sysInfo.codeSwitch')) {
            log.debug("QR code scanning is now disabled.")
            return
        }
        try {
            dxCode.worker.beforeLoop(this.options1, this.options2)
            let deType = config.get('sysInfo.de_type')
            // Special handling: Configuration codes (QR codes) must be allowed even if all code types or QR codes are disabled.
            deType = deType | 1;
            dxCode.decoderUpdateConfig({ deType })
        } catch (error) {
            common.asyncReboot(1)
        }
    },
    loop: function () {
        if (!config.get('sysInfo.codeSwitch')) {
            log.debug("QR code scanning is now disabled.")
            this.loop = () => { }
        } else {
            this.loop = () => dxCode.worker.loop(config.get('sysInfo.s_mode'), config.get('sysInfo.interval'))
        }
    }
}

driver.nfc = {
    options: { id: 'nfc1', m1: true, psam: false },
    init: function () {
        if (!config.get('sysInfo.nfc')) {
            log.debug("NFC is now disabled.")
            return
        }
        dxNfc.worker.beforeLoop(this.options)
    },
    loop: function () {
        if (!config.get('sysInfo.nfc')) {
            log.debug("NFC is now disabled.")
            this.loop = () => { }
        } else {
            this.loop = () => dxNfc.worker.loop(this.options)
        }
    },
    // Read a block of data from the M1 card
    m1cardReadBlk: function (taskFlg, blkNum, key, keyType) {
        return dxNfc.m1cardReadBlk(taskFlg, blkNum, key, keyType, this.options.id)
        // return dxNfc.m1cardReadBlk(0, 0,[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], 0x60,this.options.id)
    },
    // Write a block of data to the M1 card
    m1cardWriteBlk: function (taskFlg, blkNum, key, keyType, data) {
        return dxNfc.m1cardWriteBlk(taskFlg, blkNum, key, keyType, data, this.options.id)
        // return dxNfc.m1cardWriteBlk(0, 1, [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], 0x61, [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], this.options.id)

    },
    // Read multiple blocks of data from the M1 card
    m1cardReadSector: function (taskFlg, secNum, logicBlkNum, blkNums, key, keyType) {
        return dxNfc.m1cardReadSector(taskFlg, secNum, logicBlkNum, blkNums, key, keyType, this.options.id)
        // return dxNfc.m1cardReadSector(0, 2, 0, 4,[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], 0x60,this.options.id)
    },
    // Write multiple blocks of data to M1 card
    m1cardWriteSector: function (taskFlg, secNum, logicBlkNum, blkNums, key, keyType, data) {
        return dxNfc.m1cardWriteSector(taskFlg, secNum, logicBlkNum, blkNums, key, keyType, data, this.options.id)
        // return dxNfc.m1cardWriteBlk(0, 5, 0, 2,[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], 0x61, [0x11, 0x22, 0x33], this.options.id)
    },
}

driver.audio = {
    init: function () {
        dxAlsaplay.init()
        // Audio volume
        let volume = Math.floor(config.get("sysInfo.volume") / 10)
        dxAlsaplay.setVolume(volume)
        if (config.get('sysInfo.boot_music') == 1) {
            if (config.get('sysInfo.language') == 1) {
                this.play("0_eng")
            } else {
                this.play("0")
            }
        }
    },
    // Get/set volume, range ([0,6]), configuration is 0-60, exceeding 6 has the same effect.
    volume: function (volume) {
        if (utils.isEmpty(volume)) {
            return dxAlsaplay.getVolume()
        } else {
            dxAlsaplay.setVolume(volume)
        }
    },
    play: function (index) {
        dxAlsaplay.play('/app/code/resource/wav/' + index + '.wav')
    }
}

driver.gpiokey = {
    init: function () {
        dxGpioKey.worker.beforeLoop()
    },
    loop: function () {
        dxGpioKey.worker.loop()
    },
}

driver.ntp = {
    loop: function () {
        if (!config.get('sysInfo.ntp_en')) {
            log.debug("Automatic time synchronization is off.")
            this.loop = () => { }
        } else {
            dxNtp.beforeLoop(config.get('sysInfo.ntp_addr'), config.get('sysInfo.ntp_interval'))
            this.ntp_hour = config.get('sysInfo.ntp_hour')
            this.flag = true
            this.loop = () => {
                dxNtp.loop()
                if (new Date().getHours() == this.ntp_hour && this.flag) {
                    // Scheduled synchronization, synchronize time immediately.
                    dxNtp.syncnow = true
                    this.flag = false
                }
                if (new Date().getHours() != this.ntp_hour) {
                    // Time synchronization will be allowed again after this hour.
                    this.flag = true
                }
            }
        }
    },
}

driver.screen = {
    // success
    success: function (msg, beep) {
        bus.fire('success', { msg, beep })
    },
    // fail
    fail: function (msg, beep) {
        bus.fire('fail', { msg, beep })
    },
    // Custom pop-up window
    customPopWin: function (msg, time, hasBtn, res, hasTitle) {
        bus.fire('customPopWin', { msg, time, hasBtn, res, hasTitle })
    },
    // Display text/images
    customShowMsgAndImg: function (msg, msgTimeout, img, imgTimeout, flag) {
        bus.fire('customShowMsgAndImg', { msg, msgTimeout, img, imgTimeout, flag })
    },
    // Reload UI
    reload: function () {
        bus.fire('reload', null)
    },
    tcpConnectedChange: function (data) {
        bus.fire('tcpConnectedChange', data)
    },
    netStatusChange: function (data) {
        bus.fire('netStatusChange', data)
    },
    showPic: function (data) {
        bus.fire('customShowMsgAndImg', { msg: null, msgTimeout: null, img: data.name, imgTimeout: data.time })
    },
    showSystemInfo: function (data) {
        bus.fire('showSystemInfo', data)
    },
    timeFormat: function () {
        bus.fire('timeFormat', null)
    }
}

driver.uart485 = {
    id: 'uart485',
    init: function () {
        // Determine whether safe mode is enabled (if enabled, it can be used as a server).
        if (config.get('sysInfo.safe_open') === 1) {
            dxUart.runvg({ id: this.id, type: dxUart.TYPE.UART, path: '/dev/ttyS3', result: 1 })
        } else {
            dxUart.runvg({ id: this.id, type: dxUart.TYPE.UART, path: '/dev/ttyS3', result: 0 })
        }
        std.setTimeout(() => {
            dxUart.ioctl(6, config.get("sysInfo.p_uart1"), this.id)
        }, 1000)
    },
    // You can send a protocol JSON or pass through a string.
    sendVg: function (data) {
        // log.debug("[485 send]:", JSON.stringify(data))
        dxUart.sendVg(data, this.id)
    },
    lastHeartbeat: 0,
    heart_en: 0,
    heart_time: 30,
    heart_data: undefined,
    heartbeat: function () {
        bus.on("heart485", (data) => {
            this.heart_en = utils.isEmpty(data.heart_en) ? this.heart_en : data.heart_en
            this.heart_time = utils.isEmpty(data.heart_time) ? this.heart_time : (data.heart_time < 30 ? 30 : data.heart_time)
            this.heart_data = utils.isEmpty(data.heart_data) ? this.heart_data : data.heart_data
        })
        if (this.heart_en == 1) {
            // The minimum heart rate is 30.
            if (this.heart_time < 30) {
                this.heart_time = 30
            }
            this.heartbeat = () => {
                if (this.heart_en && new Date().getTime() - this.lastHeartbeat >= (this.heart_time * 1000)) {
                    this.lastHeartbeat = new Date().getTime()
                    let res = common.strToUtf8Hex(this.heart_data)
                    driver.uart485.sendVg(config.get('sysInfo.w_mode') == 1 ? res : { "cmd": "2b", "length": res.length / 2, "result": "00", "data": res })
                }
            }
        }
    }
}

driver.uartBle = {
    id: 'uartBle',
    init: function () {
        dxUart.runvg({ id: this.id, type: dxUart.TYPE.UART, path: '/dev/ttyS5', result: 0 })
        std.sleep(1000)
        dxUart.ioctl(6, '921600-8-N-1', this.id)
    },
    sendVg: function (data) {
        log.debug("[ble send]:", JSON.stringify(data))
        dxUart.sendVg(data, this.id)
    },
    /**
     * Get Bluetooth configuration information
     * @returns  param.name is the Bluetooth name; param.mac is the Bluetooth mac.
     */
    getConfig: function () {
        let pack = { "head": "55aa", "cmd": "60", "result": "00", "dlen": 6, "data": "7e01000200fe" }
        this.sendVg("55aa6000" + common.decimalToLittleEndianHex(pack.dlen, 2) + pack.data + this.genCrc(pack))
        return driver.sync.request("uartBle.getConfig", 2000)
    },
    setConfig: function (param) {
        uartBleService.setBleConfig(param)
    },
    /**
       * The calculation of the checksum for Bluetooth serial port is different from that of a regular checksum.
       * @param {*} pack eg:{ "head": "55aa", "cmd": "0f", "result": "90", "dlen": 1, "data": "01" }
       * @returns 
       */
    genCrc: function (pack) {
        let bcc = 0;
        let dlen = pack.dlen - 1;//Remove index
        bcc ^= 0x55;
        bcc ^= 0xaa;
        bcc ^= parseInt(pack.cmd, 16);
        bcc ^= pack.result ? parseInt(pack.result, 16) : 0;
        bcc ^= (dlen & 0xff);
        bcc ^= (dlen & 0xff00) >> 8;
        for (let i = 0; i < pack.dlen; i++) {
            bcc ^= pack.data[i];
        }
        return bcc.toString(16).padStart(2, '0');
    }
}

driver.tcp = {
    id: 'tcp',
    init: function () {
        let owifi = config.get("sysInfo.owifi")
        if ((owifi == 4 || owifi == 8) && checkTcp()) {
            dxTcp.runvg({ id: this.id, ip: config.get('sysInfo.taddr'), port: config.get('sysInfo.port') + '', timeout: config.get('sysInfo.touttime') * 1000, result: 0, passThrough: config.get("sysInfo.owifi") == 8 })
        }
    },
    // You can send a protocol JSON or pass through a string.
    sendVg: function (data) {
        log.debug("[tcp send]:", JSON.stringify(data))
        dxTcp.sendVg(data, this.id)
    },
    // Network Protocol
    sendNetPro: function (data, isHeartbeat) {
        if (!dxTcp.isConnect(this.id) && !isHeartbeat) {
            let awifi_f = config.get("sysInfo.awifi_f")
            if ((awifi_f >> 13) & 1 == 1) {
                driver.screen.customPopWin(config.get('sysInfo.language') == 1 ? "fail" : "fail", 2000, false, false)
            }
            if ((awifi_f >> 11) & 1 == 1) {
                if (config.get('sysInfo.language') == 1) {
                    driver.audio.play('f_eng')
                } else {
                    driver.audio.play('f')
                }
            }
            return
        }
        data = common.stringToHex(`vgdecoderesult=`) + data + common.stringToHex(`&&devicenumber=${config.get("sysInfo.devnum")}&&otherparams=`)
        this.sendVg(data)
        if (!isHeartbeat) {
            // Heartbeat reporting does not receive responses
            let map = dxMap.get("NETPRO")
            map.put("waitResponse", new Date().getTime())
            std.setTimeout(() => {
                // time out
                let waitResponse = map.get("waitResponse")
                if (waitResponse) {
                    // A pending request failed to be answered.
                    map.del("waitResponse")
                    netProService.netRes({ source: 'tcp', data: "" })
                }
            }, config.get("sysInfo.touttime") * 1000)
        }
    }
}

driver.http = {
    // You can send a protocol JSON or pass through a string.
    sendVg: function (data) {
        // Convert to string
        if (typeof data === 'object') {
            let pack = '55aa' + data.cmd
            if (data.hasOwnProperty('result')) {
                pack += data.result
            }
            pack += (data.length % 256).toString(16).padStart(2, '0')
            pack += (Math.floor(data.length / 256)).toString(16).padStart(2, '0')
            pack += data.data
            let all = common.hexToArr(pack)
            let bcc = common.calculateBcc(all)
            all.push(bcc)
            data = all.map(v => v.toString(16).padStart(2, '0')).join('')
        }
        data = common.utf8HexToStr(data)
        // log.debug("[http post]:", JSON.stringify(data))
        let headers = []
        headers[0] = "Accept-Charset:utf-8"
        headers[1] = "Content-Type:text/html;charset=UTF-8"
        headers[2] = "Connection:close"
        return http.post(config.get('sysInfo.haddr'), data, headers, (config.get('sysInfo.houttime') || 2) * 1000)
    },
    // Network Protocol
    sendNetPro: function (data, isHeartbeat) {
        // Network Protocol Mode
        data = common.stringToHex("vgdecoderesult=") + data + common.stringToHex("&&devicenumber=" + config.get("sysInfo.devnum")) + common.stringToHex("&&otherparams=")
        let res = this.sendVg(data)
        log.debug("[http response]:", res)
        if (!res) {
            log.error("The HTTP request returned an empty string.")
            driver.screen.customPopWin("fail", 2000, false, false)
            driver.pwm.beep()
            driver.pwm.beep(500, null, 1)
        }
        if (!isHeartbeat) {
            // Heartbeat reporting does not receive responses
            netProService.netRes({ source: 'http', data: JSON.parse(res).body })
        }
    }
}

// Note that it can only be used in the service thread.
driver.sync = {
    // A simple implementation of asynchronous to synchronous conversion
    request: function (topic, timeout) {
        let map = dxMap.get("SYNC")
        let count = 0
        let data = map.get(topic)
        while ((utils.isEmpty(data) || data == "") && count * 100 < timeout) {
            data = map.get(topic)
            std.sleep(100)
            count += 1
        }
        let res = map.get(topic)
        map.del(topic)
        return res
    },
    response: function (topic, data) {
        let map = dxMap.get("SYNC")
        map.put(topic, data)
    }
}

driver.config = {
    init: function () {
        config.init()
        let uuid = common.getSn(19)
        if (!config.get('sysInfo.uuid') && uuid) {
            config.set('sysInfo.uuid', uuid)
        }
        // If sn is empty, use the device UUID first.
        if (!config.get('sysInfo.sn') && uuid) {
            config.set('sysInfo.sn', uuid)
        }
        config.save()
    }
}

driver.heartbeat = {
    lastHeartbeat: 0,
    heart_en: config.get('sysInfo.heart_en'),
    heart_time: config.get('sysInfo.heart_time'),
    heart_data: config.get('sysInfo.heart_data'),
    loop: function () {
        bus.on("heartbeat", (data) => {
            this.heart_en = utils.isEmpty(data.heart_en) ? this.heart_en : data.heart_en
            this.heart_time = utils.isEmpty(data.heart_time) ? this.heart_time : (data.heart_time < 30 ? 30 : data.heart_time)
            this.heart_data = utils.isEmpty(data.heart_data) ? this.heart_data : data.heart_data
        })
        if (this.heart_en) {
            // The minimum heart rate is 30.
            if (this.heart_time < 30) {
                this.heart_time = 30
            }
            this.loop = () => {
                if (this.heart_en && new Date().getTime() - this.lastHeartbeat >= (this.heart_time * 1000)) {
                    this.lastHeartbeat = new Date().getTime()
                    let res = common.strToUtf8Hex(config.get('sysInfo.heart_data'))
                    driver.passage.report({ source: 'heartbeat', data: { "cmd": "2b", "length": res.length / 2, "result": "00", "data": res } })
                }
            }
        }
    }
}

driver.watchdog = {
    id: "watchdog",
    init: function () {
        watchdog.open(1 | 2, this.id)
        watchdog.enable(1, this.id)
        if (config.get('sysInfo.codeSwitch')) {
            watchdog.enable(2, this.id)
        }
        watchdog.start(20000, this.id)
    },
    loop: function () {
        watchdog.loop(1, this.id)
    },
    feed: function (flag, timeout) {
        watchdog.feed(flag, timeout)
    }
}

// All channels that have been opened, whether through transparent transmission or micro-light protocol (card swiping, QR code scanning, Bluetooth, heartbeat), must be reported uniformly.
driver.passage = {
    // Select the reporting channel according to the configuration.
    report: function (data) {
        switch (data.source) {
            case 'code':
            case 'heartbeat':
            case 'password':
                // Output Channel
                let ochannel = config.get('sysInfo.ochannel')
                // protocol
                let dchannel = config.get('sysInfo.dchannel')
                driver.passage.send(ochannel, dchannel, data.data, data.source == 'heartbeat')
                break;
            case 'nfc':
                // Output Channel
                let nochannel = config.get('sysInfo.nochannel')
                // protocol
                let ndchannel = config.get('sysInfo.ndchannel')
                driver.passage.send(nochannel, ndchannel, data.data)
                break;
            case 'ble':
                // Output Channel
                let blochannel = config.get('sysInfo.blochannel')
                // protocol
                let bldchannel = config.get('sysInfo.bldchannel')
                driver.passage.send(blochannel, bldchannel, data.data)
                break;
            default:
                return
        }
    },
    // Determine output protocol and report to the corresponding channel.
    send: function (ochannel, dchannel, data, isHeartbeat) {
        let w_mode = config.get('sysInfo.w_mode')
        if (w_mode == 1) {
            // In pass-through mode, the 55aa format is not required when sending.
            data = data.data
        }
        if ((ochannel >> 1) & 0x01 && !isHeartbeat) {
            // 485 output
            driver.uart485.sendVg(data)
        }
        if ((ochannel >> 6) & 0x01) {
            // Wired network output, TCP/HTTP
            // Determine protocol, single choice
            let owifi = config.get("sysInfo.owifi")
            switch (owifi) {
                case 1:
                    // Normal HTTP mode
                    if (w_mode == 2) {
                        // In protocol mode, only 485 and TCP support output; HTTP, HTTP protocol, and TCP protocol do not output content.
                        break
                    }
                    driver.http.sendVg(data)
                    break;
                case 2:
                    // HTTP protocol mode
                    if (w_mode == 2) {
                        // In protocol mode, only 485 and TCP support output; HTTP, HTTP protocol, and TCP protocol do not output content.
                        break
                    }
                    driver.http.sendNetPro(typeof data == 'object' ? data.data : data, isHeartbeat, w_mode)
                    break;
                case 4:
                    // Normal TCP mode
                    driver.tcp.sendVg(data)
                    break;
                case 8:
                    // TCP protocol mode
                    if (w_mode == 2) {
                        // In protocol mode, only 485 and TCP support output; HTTP, HTTP protocol, and TCP protocol do not output content.
                        break
                    }
                    driver.tcp.sendNetPro(typeof data == 'object' ? data.data : data, isHeartbeat, w_mode)
                    break;
                default:
                    break;
            }
        }
    },
    // Scanning QR codes, swiping cards, using passwords, and Bluetooth—all via the 30/33 interface.
    beforeReport: function (data) {
        let sourceMap = {
            'code': "11",
            'password': "a0",
            'nfc': "42",
            'ble': "80",
        }
        switch (config.get('sysInfo.report_mode')) {
            case '0'://Regardless of source
            case '80'://Distinguish from source
                //The host computer polls 0x33 to obtain scanner data.
                //Store the scanned data in the cache and wait for the host computer to actively request it.
                if (config.get('sysInfo.w_mode') == 1) {
                    driver.passage.report({ source: data.source, data: { "cmd": "30", "length": data.data.length / 2, "result": "00", "data": data.data } })
                } else {
                    let map = dxMap.get("VGPRO")
                    map.put('codeReport', { sourceMark: sourceMap[data.source], data: data.data, time: new Date().getTime() })
                }
                break;
            case '1':
                //The 0x30 instruction is used to actively report data, regardless of its source.
                driver.passage.report({ source: data.source, data: { "cmd": "30", "length": data.data.length / 2, "result": "00", "data": data.data } })
                break;
            case '81':
                // The 0x33 instruction is used to actively report data, distinguishing the source and representing the QR code identifier.
                driver.passage.report({ source: data.source, data: { "cmd": "33", "length": data.data.length / 2 + 1, "result": "00", "data": config.get('sysInfo.w_mode') == 1 ? data.data : sourceMap[data.source] + data.data } })
                break;
            default:
                return
        }
    }
}
function checkTcp () {
    // Channel name to be checked
    const channels = ["sysInfo.ochannel", "sysInfo.nochannel", "sysInfo.blochannel"];
    // Mask for bitwise AND operation with channel values
    const masks = [3, 6, 9];

    // Check each channel and each mask
    return channels.some(channel =>
        masks.some(mask =>
            (config.get(channel) >> mask) & 1 === 1
        )
    );
}

driver.autoRestart = {
    lastRestartCheck: new Date().getHours(),  // Initialize to the current hour, not 0.
    init: function () {
        std.setInterval(() => {        // Check if a restart is required at the top of the hour.
            const now = new Date()
            const currentHour = now.getHours()
            // The operation will only be executed if the number of hours equals the set value and is not the hour last checked.
            let autoRestart = utils.isEmpty(config.get("sysInfo.autoRestart")) ? 3 : config.get("sysInfo.autoRestart")
            if (currentHour === autoRestart && currentHour !== this.lastRestartCheck && now.getMinutes() === 0) {
                common.systemBrief('reboot')
            }
            // Update the number of hours since the last check.
            this.lastRestartCheck = currentHour
        }, 60000)
    }
}
export default driver
